import 'intersection-observer'
import { useState } from 'react'
import {
  type BasicTarget,
  getTargetElement,
} from '../utils/dom-target'
import { useEffectWithTarget } from './useEffect-with-target'

type CallbackType = (entry: IntersectionObserverEntry) => void;

export interface Options {
  /** 类似于CSS的margin，用于扩大或缩小根元素的边界框。 写法同css margin */
  rootMargin?: string;
  /** 交叉比例的阈值，可以是单个值或数组。 0 - 1 */
  threshold?: number | number[];
  /** 用作视口的元素. 默认为浏览器视口 */
  root?: BasicTarget<Element>;
  /** callback. 不属于 */
  callback?: CallbackType;
}

export function useInViewport(target: BasicTarget | BasicTarget[], options?: Options): readonly [boolean | undefined, number | undefined] {
  const { callback, ...option } = options || {}

  const [state, setState] = useState<boolean>()
  const [ratio, setRatio] = useState<number>()

  useEffectWithTarget(
    () => {
      const targets = Array.isArray(target) ? target : [target]
      // 所有假值（falsy）：undefined、null、0、''、NaN、false 全部被剔除
      const els = targets.map((element) => getTargetElement(element)).filter(Boolean)

      if (!els.length) {
        return
      }

      const observer = new IntersectionObserver(
        (entries) => {
          /**
           * entry = {
              // 目标元素本身
              target: Element,
              // 交叉比例 (0~1)
              intersectionRatio: 0.75,
              // 是否正在交叉
              isIntersecting: true,
              // 交叉发生的时间戳
              time: 1234.56
            }
           */
          for (const entry of entries) {
            setRatio(entry.intersectionRatio)
            setState(entry.isIntersecting)
            callback?.(entry)
          }
        },
        {
          ...option,
          root: getTargetElement(options?.root),
        },
      )

      els.forEach((el) => {
        if (el) {
          observer.observe(el)
        }
      })

      return () => {
        observer.disconnect()
      }
    },
    [options?.rootMargin, options?.threshold, options?.root, callback],
    target,
  )

  return [state, ratio] as const
}
